یک بررسی عمیق از چارچوب تست جنگو، مقایسه و تضاد TestCase و TransactionTestCase برای کمک به شما در نوشتن تستهای موثرتر و قابل اعتمادتر.
آزمایش جنگو پایتون: TestCase در مقابل TransactionTestCase
آزمایش یک جنبه حیاتی از توسعه نرمافزار است که اطمینان میدهد برنامه شما همانطور که انتظار میرود رفتار میکند و در طول زمان قوی میماند. جنگو، یک فریمورک وب محبوب پایتون، یک چارچوب آزمایشی قدرتمند برای کمک به شما در نوشتن تستهای موثر ارائه میدهد. این پست وبلاگ به دو کلاس اساسی در چارچوب تست جنگو میپردازد: TestCase
و TransactionTestCase
. ما تفاوتها، موارد استفاده و مثالهای عملی آنها را بررسی میکنیم تا به شما کمک کنیم کلاس مناسبی را برای نیازهای آزمایشی خود انتخاب کنید.
چرا آزمایش در جنگو مهم است
قبل از ورود به جزئیات TestCase
و TransactionTestCase
، بیایید به طور خلاصه در مورد اینکه چرا آزمایش در توسعه جنگو بسیار مهم است، بحث کنیم:
- تضمین کیفیت کد: تستها به شما کمک میکنند اشکالات را در مراحل اولیه توسعه شناسایی کنید و از ورود آنها به تولید جلوگیری کنید.
- تسهیل بازسازی: با یک مجموعه تست جامع، میتوانید با اطمینان کد خود را بازسازی کنید، با دانستن اینکه تستها در صورت معرفی هرگونه رگرسیون به شما هشدار میدهند.
- بهبود همکاری: تستهای خوب نوشته شده به عنوان مستندات برای کد شما عمل میکنند و درک و مشارکت را برای توسعهدهندگان دیگر آسانتر میکنند.
- پشتیبانی از توسعه مبتنی بر تست (TDD): TDD یک رویکرد توسعه است که در آن شما قبل از نوشتن کد واقعی، تست مینویسید. این شما را مجبور میکند که از قبل در مورد رفتار مورد نظر برنامه خود فکر کنید و منجر به کد تمیزتر و قابل نگهداریتر میشود.
چارچوب تست جنگو: یک نمای کلی سریع
چارچوب تست جنگو بر اساس ماژول unittest
داخلی پایتون ساخته شده است. این ویژگیهای مختلفی را ارائه میدهد که آزمایش برنامههای جنگو را آسانتر میکند، از جمله:
- کشف تست: جنگو به طور خودکار تستها را در پروژه شما کشف و اجرا میکند.
- دونده تست: جنگو یک دونده تست ارائه میدهد که تستهای شما را اجرا و نتایج را گزارش میکند.
- روشهای تأیید: جنگو مجموعهای از روشهای تأیید را ارائه میدهد که میتوانید از آنها برای تأیید رفتار مورد انتظار کد خود استفاده کنید.
- مشتری: مشتری تست جنگو به شما امکان میدهد تعاملات کاربر با برنامه خود را شبیهسازی کنید، مانند ارسال فرم یا ایجاد درخواستهای API.
- TestCase و TransactionTestCase: این دو کلاس اساسی برای نوشتن تست در جنگو هستند که ما آنها را با جزئیات بررسی خواهیم کرد.
TestCase: تست واحد سریع و کارآمد
TestCase
کلاس اصلی برای نوشتن تستهای واحد در جنگو است. این یک محیط پایگاه داده تمیز برای هر مورد تست فراهم میکند، و اطمینان میدهد که تستها ایزوله هستند و با یکدیگر تداخل ندارند.
نحوه عملکرد TestCase
هنگامی که از TestCase
استفاده میکنید، جنگو مراحل زیر را برای هر روش تست انجام میدهد:
- ایجاد یک پایگاه داده تست: جنگو یک پایگاه داده تست جداگانه برای هر اجرا تست ایجاد میکند.
- خالی کردن پایگاه داده: قبل از هر روش تست، جنگو پایگاه داده تست را خالی میکند و تمام دادههای موجود را حذف میکند.
- اجرای روش تست: جنگو روش تست تعریف شده شما را اجرا میکند.
- بازگرداندن تراکنش: پس از هر روش تست، جنگو تراکنش را بازمیگرداند، که به طور موثر هرگونه تغییر ایجاد شده در پایگاه داده در طول تست را خنثی میکند.
این رویکرد تضمین میکند که هر روش تست با یک صفحه تمیز شروع میشود و هرگونه تغییر ایجاد شده در پایگاه داده به طور خودکار بازگردانده میشود. این امر TestCase
را برای تست واحد ایدهآل میکند، جایی که میخواهید اجزای جداگانه برنامه خود را به صورت مجزا آزمایش کنید.
مثال: آزمایش یک مدل ساده
بیایید یک مثال ساده از آزمایش یک مدل جنگو با استفاده از TestCase
در نظر بگیریم:
from django.test import TestCase
from .models import Product
class ProductModelTest(TestCase):
def test_product_creation(self):
product = Product.objects.create(name="Test Product", price=10.00)
self.assertEqual(product.name, "Test Product")
self.assertEqual(product.price, 10.00)
self.assertTrue(isinstance(product, Product))
در این مثال، ما در حال آزمایش ایجاد یک نمونه مدل Product
هستیم. متد test_product_creation
یک محصول جدید ایجاد میکند و سپس از متدهای تأیید برای تأیید اینکه ویژگیهای محصول به درستی تنظیم شدهاند، استفاده میکند.
چه زمانی از TestCase استفاده کنیم
TestCase
به طور کلی انتخاب ترجیحی برای اکثر سناریوهای تست جنگو است. سریع، کارآمد است و یک محیط پایگاه داده تمیز برای هر تست ارائه میدهد. زمانی از TestCase
استفاده کنید که:
- در حال آزمایش مدلها، نماها یا سایر اجزای برنامه خود هستید.
- میخواهید اطمینان حاصل کنید که تستهای شما ایزوله هستند و با یکدیگر تداخل ندارند.
- نیازی به آزمایش تعاملات پیچیده پایگاه داده ندارید که چندین تراکنش را شامل میشوند.
TransactionTestCase: آزمایش تعاملات پیچیده پایگاه داده
TransactionTestCase
کلاس دیگری برای نوشتن تست در جنگو است، اما در نحوه مدیریت تراکنشهای پایگاه داده با TestCase
متفاوت است. به جای بازگرداندن تراکنش پس از هر روش تست، TransactionTestCase
تراکنش را متعهد میکند. این باعث میشود برای آزمایش تعاملات پیچیده پایگاه داده که چندین تراکنش را شامل میشوند، مانند مواردی که شامل سیگنالها یا تراکنشهای اتمی هستند، مناسب باشد.
نحوه عملکرد TransactionTestCase
هنگامی که از TransactionTestCase
استفاده میکنید، جنگو مراحل زیر را برای هر مورد تست انجام میدهد:
- ایجاد یک پایگاه داده تست: جنگو یک پایگاه داده تست جداگانه برای هر اجرا تست ایجاد میکند.
- پایگاه داده را خالی نمیکند: TransactionTestCase به طور خودکار پایگاه داده را قبل از هر تست خالی *نمیکند*. انتظار دارد که پایگاه داده قبل از اجرای هر تست در یک حالت سازگار باشد.
- اجرای روش تست: جنگو روش تست تعریف شده شما را اجرا میکند.
- تعهد تراکنش: پس از هر روش تست، جنگو تراکنش را متعهد میکند و تغییرات را در پایگاه داده تست دائمی میکند.
- حذف جداول: در *پایان* تمام تستها در TransactionTestCase، جداول برای پاک کردن دادهها حذف میشوند.
از آنجایی که TransactionTestCase
تراکنش را پس از هر روش تست متعهد میکند، اطمینان از اینکه تستهای شما پایگاه داده را در یک حالت ناسازگار رها نمیکنند، ضروری است. ممکن است لازم باشد هر دادهای را که در طول تست ایجاد شده است، به صورت دستی پاک کنید تا از تداخل با تستهای بعدی جلوگیری کنید.
مثال: آزمایش سیگنالها
بیایید مثالی از آزمایش سیگنالهای جنگو با استفاده از TransactionTestCase
در نظر بگیریم:
from django.test import TransactionTestCase
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Product, ProductLog
@receiver(post_save, sender=Product)
def create_product_log(sender, instance, created, **kwargs):
if created:
ProductLog.objects.create(product=instance, action="Created")
class ProductSignalTest(TransactionTestCase):
def test_product_creation_signal(self):
product = Product.objects.create(name="Test Product", price=10.00)
self.assertEqual(ProductLog.objects.count(), 1)
self.assertEqual(ProductLog.objects.first().product, product)
self.assertEqual(ProductLog.objects.first().action, "Created")
در این مثال، ما در حال آزمایش یک سیگنال هستیم که هر زمان که یک نمونه Product
جدید ایجاد میشود، یک نمونه ProductLog
ایجاد میکند. متد test_product_creation_signal
یک محصول جدید ایجاد میکند و سپس تأیید میکند که یک ورودی گزارش محصول مربوطه ایجاد شده است.
چه زمانی از TransactionTestCase استفاده کنیم
TransactionTestCase
معمولاً در سناریوهای خاصی استفاده میشود که در آن نیاز به آزمایش تعاملات پیچیده پایگاه داده دارید که چندین تراکنش را شامل میشوند. در صورت استفاده از TransactionTestCase
موارد زیر را در نظر بگیرید:
- در حال آزمایش سیگنالهایی هستید که توسط عملیات پایگاه داده راهاندازی میشوند.
- در حال آزمایش تراکنشهای اتمی هستید که شامل چندین عملیات پایگاه داده میشوند.
- شما نیاز دارید که وضعیت پایگاه داده را پس از یک سری عملیات مرتبط تأیید کنید.
- شما از کدی استفاده میکنید که به شناسه افزایش خودکار متکی است تا بین تستها حفظ شود (اگرچه این معمولاً یک عمل بد در نظر گرفته میشود).
نکات مهم در هنگام استفاده از TransactionTestCase
از آنجایی که TransactionTestCase
تراکنشها را متعهد میکند، آگاهی از نکات زیر مهم است:
- پاکسازی پایگاه داده: ممکن است لازم باشد هر دادهای را که در طول تست ایجاد شده است، به صورت دستی پاک کنید تا از تداخل با تستهای بعدی جلوگیری کنید. در نظر بگیرید که از متدهای
setUp
وtearDown
برای مدیریت دادههای تست استفاده کنید. - ایزوله کردن تست:
TransactionTestCase
همان سطح ایزولهسازی تست را کهTestCase
فراهم میکند، ارائه نمیدهد. از تعاملات احتمالی بین تستها آگاه باشید و اطمینان حاصل کنید که تستهای شما به وضعیت پایگاه داده از تستهای قبلی متکی نیستند. - عملکرد:
TransactionTestCase
میتواند کندتر ازTestCase
باشد زیرا شامل تعهد تراکنشها میشود. با احتیاط و فقط در صورت لزوم از آن استفاده کنید.
بهترین روشها برای تست جنگو
در اینجا برخی از بهترین روشها وجود دارد که باید هنگام نوشتن تستها در جنگو در نظر داشته باشید:
- نوشتن تستهای واضح و مختصر: تستها باید درک و نگهداری آنها آسان باشد. از نامهای توصیفی برای روشهای تست و تأییدها استفاده کنید.
- یک چیز را در یک زمان تست کنید: هر متد تست باید بر روی تست یک جنبه از کد شما متمرکز شود. این کار شناسایی منبع شکست را در صورت شکست تست آسانتر میکند.
- از تأییدهای معنادار استفاده کنید: از روشهای تأییدی استفاده کنید که به وضوح رفتار مورد انتظار کد شما را بیان میکنند. جنگو مجموعهای غنی از روشهای تأیید برای سناریوهای مختلف ارائه میدهد.
- از الگوی Arrange-Act-Assert پیروی کنید: تستهای خود را مطابق با الگوی Arrange-Act-Assert ساختاردهی کنید: دادههای تست را مرتب کنید، بر روی کد تحت آزمایش عمل کنید و نتیجه مورد انتظار را تأیید کنید.
- تستهای خود را سریع نگه دارید: تستهای کند میتوانند توسعهدهندگان را از اجرای مکرر آنها دلسرد کنند. تستهای خود را بهینه کنید تا زمان اجرا را به حداقل برسانید.
- از ثابتها برای دادههای تست استفاده کنید: ثابتها یک راه مناسب برای بارگذاری دادههای اولیه در پایگاه داده تست شما هستند. از ثابتها برای ایجاد دادههای تست سازگار و قابل استفاده مجدد استفاده کنید. در نظر بگیرید که از کلیدهای طبیعی در ثابتها برای جلوگیری از کدنویسی IDها استفاده کنید.
- در نظر بگیرید که از یک کتابخانه تست مانند pytest استفاده کنید: در حالی که چارچوب تست داخلی جنگو قدرتمند است، کتابخانههایی مانند pytest میتوانند ویژگیها و انعطافپذیری بیشتری را ارائه دهند.
- برای پوشش تست بالا تلاش کنید: هدف پوشش تست بالا را داشته باشید تا اطمینان حاصل کنید که کد شما به طور کامل آزمایش شده است. از ابزارهای پوشش برای اندازهگیری پوشش تست خود و شناسایی مناطقی که به تست بیشتری نیاز دارند استفاده کنید.
- تستها را در خط لوله CI/CD خود ادغام کنید: تستهای خود را به طور خودکار به عنوان بخشی از خط لوله یکپارچهسازی مداوم و استقرار مداوم (CI/CD) اجرا کنید. این امر تضمین میکند که هرگونه رگرسیون در مراحل اولیه توسعه شناسایی میشود.
- تستهایی بنویسید که سناریوهای دنیای واقعی را منعکس کنند: برنامه خود را به روشهایی آزمایش کنید که نحوه تعامل کاربران با آن را شبیهسازی میکند. این به شما کمک میکند اشکالاتی را کشف کنید که ممکن است در تستهای واحد ساده آشکار نباشند. به عنوان مثال، هنگام تست فرمها، تغییرات در آدرسها و شماره تلفنهای بینالمللی را در نظر بگیرید.
بینالمللیسازی (i18n) و آزمایش
هنگام توسعه برنامههای جنگو برای مخاطبان جهانی، در نظر گرفتن بینالمللیسازی (i18n) و محلیسازی (l10n) بسیار مهم است. اطمینان حاصل کنید که تستهای شما زبانهای مختلف، قالبهای تاریخ و نمادهای ارزی را پوشش میدهند. در اینجا چند نکته آورده شده است:
- با تنظیمات زبان مختلف تست کنید: از دکوراتور
override_settings
جنگو برای آزمایش برنامه خود با تنظیمات زبان مختلف استفاده کنید. - از دادههای محلیشده در تستهای خود استفاده کنید: از دادههای محلیشده در ثابتها و روشهای تست خود استفاده کنید تا اطمینان حاصل شود که برنامه شما قالبهای تاریخ، نمادهای ارزی و سایر دادههای خاص محل را به درستی مدیریت میکند.
- رشتههای ترجمه خود را تست کنید: تأیید کنید که رشتههای ترجمه شما به درستی ترجمه شدهاند و در زبانهای مختلف به درستی ارائه میشوند.
- از برچسب قالب
localize
استفاده کنید: در الگوهای خود، از برچسب قالبlocalize
برای قالببندی تاریخها، اعداد و سایر دادههای خاص محل مطابق با محل فعلی کاربر استفاده کنید.
مثال: آزمایش با تنظیمات زبان مختلف
from django.test import TestCase
from django.utils import translation
from django.conf import settings
class InternationalizationTest(TestCase):
def test_localized_date_format(self):
original_language = translation.get_language()
try:
translation.activate('de') # Activate German language
with self.settings(LANGUAGE_CODE='de'): # Set the language in settings
from django.utils import formats
from datetime import date
d = date(2024, 1, 20)
formatted_date = formats.date_format(d, 'SHORT_DATE_FORMAT')
self.assertEqual(formatted_date, '20.01.2024')
finally:
translation.activate(original_language) # Restore original language
این مثال نحوه تست قالببندی تاریخ با تنظیمات زبان مختلف با استفاده از ماژولهای translation
و formats
جنگو را نشان میدهد.
نتیجه
درک تفاوتهای بین TestCase
و TransactionTestCase
برای نوشتن تستهای موثر و قابل اعتماد در جنگو ضروری است. TestCase
به طور کلی انتخاب ترجیحی برای اکثر سناریوهای تست است و یک راه سریع و کارآمد برای آزمایش اجزای جداگانه برنامه خود به صورت مجزا ارائه میدهد. TransactionTestCase
برای آزمایش تعاملات پیچیده پایگاه داده که چندین تراکنش را شامل میشوند، مانند مواردی که شامل سیگنالها یا تراکنشهای اتمی هستند، مفید است. با پیروی از بهترین روشها و در نظر گرفتن جنبههای بینالمللیسازی، میتوانید یک مجموعه تست قوی ایجاد کنید که کیفیت و قابلیت نگهداری برنامههای جنگو شما را تضمین میکند.